﻿#pragma once
#include"com.hpp"
#include"centralCache.hpp"

class ThreadCache {
public:
	void* Malloc(size_t size)//获取size大小空间
	{
		//因为如果每个字节都有一个桶，桶数量太多一定会影响性能，所以我们要使用内存对齐规则来减少桶个数
		size_t alignSize = CaculateSize::getAlignSize(size);//获得对齐后桶每个节点的大小
		size_t BucketPos = CaculateSize::getBucketPos(size);//获得对齐后桶对应的位置

		if (!_FreeBucket[BucketPos].Empty())//如果freelist中有节点，则优先使用它
		{
			return _FreeBucket[BucketPos].pop();
		}
		else
		{
			return FetchFromCentralCache(BucketPos, alignSize);//free队列中没有节点则从上层获取空间
		}
	}

	void Free(void* obj, size_t size)
	{
		assert(obj);

		size_t BucketPos = CaculateSize::getBucketPos(size);//找到对应的桶
		_FreeBucket[BucketPos].push(obj);

		if (_FreeBucket[BucketPos]._size >= _FreeBucket[BucketPos]._fetchSizeOnce)//通过链表长度来判断是否还内存
		{
			FreeListTooLong(_FreeBucket[BucketPos], size);//这里的size是一个小空间的大小，不是要释放的数量
		}

	}

	void FreeListTooLong(FreeList& list, size_t size)
	{
		void* begin = nullptr;
		void* end = nullptr;
		list.popSpace(begin, end, list._fetchSizeOnce);//返回一次申请的大小，真正释放的小空间数

		CentralCache::getCentualCache()->BackToSpans(begin, end, size);
	}

	size_t slowStart(size_t BucketPos, size_t alignSize)//仿慢启动算法
	{
		size_t& defaultSize = _FreeBucket[BucketPos].fetchSizeOnce();//默认申请大小
		size_t getSpaceNum = min(defaultSize, CaculateSize::GetMaxSize(alignSize));//设置上线防止申请过多空间
		if (defaultSize == getSpaceNum)//慢启动的速率可调节+=来修改
			defaultSize += 1;
		return getSpaceNum;
	}

	void* FetchFromCentralCache(size_t BucketPos, size_t alignSize)// 从底层获取空间
	{
		size_t getSpaceNum = slowStart(BucketPos, alignSize);//通过仿慢启动算法来确定最后的申请大小

		void* begin = nullptr;
		void* end = nullptr;
		size_t actulSize = CentralCache::getCentualCache()->FetchFromPageCache(begin, end, getSpaceNum, alignSize);//开始真正获取空间
		if (actulSize != 1 && begin != end)
		{
			//先将多申请的空间放入freelist中
			_FreeBucket[BucketPos].pushSpace(GetNext(begin), end, actulSize-1);
		}

		return begin;

	}


private:
	FreeList _FreeBucket[BucketNum];//这是一个哈希桶的结构，存储节点大小不同的freelist，整个空间最大为256KB
};


//这里创建公共的pTLSThreadCache我觉得也可以在每个线程开头创建，就不需要使用这个TLS了，稍后实验一下
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
